package com.jisf.driver.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.jisf.driver.entity.enumtype.CoursePeriodEnum;
import com.jisf.driver.entity.po.*;
import com.jisf.driver.entity.vo.CourseCoachVo;
import com.jisf.driver.entity.vo.CourseListVo;
import com.jisf.driver.entity.vo.StudentCourseVo;
import com.jisf.driver.exception.DrivingSchoolException;
import com.jisf.driver.exception.SystemException;
import com.jisf.driver.mapper.*;
import com.jisf.driver.response.ResultCode;
import com.jisf.driver.service.ICourseService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jisf.driver.utils.DateUtil;
import com.jisf.driver.utils.LogUtil;
import com.jisf.driver.utils.ParameterUtil;
import com.jisf.driver.utils.UserUtil;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import java.text.ParseException;
import java.util.*;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author jisf
 * @since 2022-05-06
 */
@Service
public class CourseServiceImpl extends ServiceImpl<CourseMapper, Course> implements ICourseService {

    @Autowired
    private CourseMapper courseMapper;

    @Autowired
    private StudentCoachMapper studentCoachMapper;

    @Autowired
    private CoachMapper coachMapper;
    
    @Autowired
    private StudentMapper studentMapper;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private LogUtil log;

    /**
     * 获取已预约课程
     * @return
     */
    @Override
    public List<StudentCourseVo> getStudentCourse() {
        try {
            Integer userId = UserUtil.userIdThreadLocal.get();
            return courseMapper.getStudentCourse(userId);
        }catch (Exception e){
            throw new SystemException(e);
        }
    }

    /**
     * 获取课程教练信息
     * @param coachId
     * @return
     */
    @Override
    public CourseCoachVo getCourseCoach(Integer coachId) {
        try {
            return courseMapper.getCourseCoach(coachId);
        }catch (Exception e){
            throw new SystemException(e);
        }
    }

    /**
     * 取消预约的课程
     * @param id
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public ResultCode cancelCourse(Integer id) {
        try {
            // 取消预约课程
            Integer studentUserid = UserUtil.userIdThreadLocal.get();
            Course course = courseMapper.selectById(id);
            Integer coachUserid = course.getCoachUserid();
            Date courseDate = course.getCourseDate();
            Integer coursePeriod = course.getCoursePeriod().getCode();
            Date courseStartDate = getCourseStartDate(courseDate, coursePeriod);
            Date date = new Date();

            // 存储日志
            String operator = UserUtil.usernameThreadLocal.get();
            String coachName = userMapper.selectById(coachUserid).getUsername();
            String msg = String.format("用户名为%s的学员取消用户名为%s的教练的课程", operator, coachName);

            long l = courseStartDate.getTime() - date.getTime();
            if (l < 0){
                String drivingSchoolLog = JSONObject.toJSONString(new DrivingSchoolLog(operator, msg, false));
                log.info(drivingSchoolLog);
                return ResultCode.COURSE_STALE_DATED;
            }
            if (l < 1000 * 3600L * ParameterUtil.CourseChangeTime){
                String drivingSchoolLog = JSONObject.toJSONString(new DrivingSchoolLog(operator, msg, false));
                log.info(drivingSchoolLog);
                return ResultCode.COURSE_CANCEL_DATE_FAILED;
            }
            int delete = courseMapper.deleteById(id);

            // 返还剩余课时
            QueryWrapper<Coach> coachQueryWrapper = new QueryWrapper<>();
            coachQueryWrapper.eq("userid", coachUserid);
            Coach coach = coachMapper.selectOne(coachQueryWrapper);
            int subject = coach.getSubject().getCode();
            QueryWrapper<Student> studentQueryWrapper = new QueryWrapper<>();
            studentQueryWrapper.eq("userid", studentUserid);
            Student student = studentMapper.selectOne(studentQueryWrapper);
            Integer subjectTime;
            switch (subject) {
                case 1:
                    subjectTime = student.getSubjectOneTime();
                    student.setSubjectOneTime(subjectTime + 1);
                    break;
                case 2:
                    subjectTime = student.getSubjectTwoTime();
                    student.setSubjectTwoTime(subjectTime + 1);
                    break;
                case 3:
                    subjectTime = student.getSubjectThreeTime();
                    student.setSubjectThreeTime(subjectTime + 1);
                    break;
                case 4:
                    subjectTime = student.getSubjectFourTime();
                    student.setSubjectFourTime(subjectTime + 1);
                    break;
                default:
                    throw new IllegalStateException("Unexpected value: " + subject);
            }
            int update = studentMapper.updateById(student);

            if (delete == 0 || update == 0){
                String drivingSchoolLog = JSONObject.toJSONString(new DrivingSchoolLog(operator, msg, false));
                log.info(drivingSchoolLog);
                TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                return ResultCode.COURSE_CANCEL_FAILED;
            }
            String drivingSchoolLog = JSONObject.toJSONString(new DrivingSchoolLog(operator, msg, true));
            log.info(drivingSchoolLog);
            return ResultCode.COURSE_CANCEL_SUCCESS;
        }catch (ParseException e){
            throw new DrivingSchoolException("日期格式异常", e);
        }catch (DrivingSchoolException e){
            throw new DrivingSchoolException(ResultCode.COURSE_CANCEL_FAILED);
        }catch (Exception e){
            throw new SystemException(e);
        }
    }

    /**
     * 获取课程开始时间
     * @param courseDate
     * @param coursePeriod
     * @return
     * @throws ParseException
     */
    private Date getCourseStartDate(Date courseDate, Integer coursePeriod) throws ParseException {
        String name = Objects.requireNonNull(CoursePeriodEnum.getEnum(coursePeriod)).getName();
        String format = DateUtil.YearToDay.format(courseDate);
        String courseTime = format + " " + name.substring(0, name.indexOf(" "));
        return DateUtil.YearToMinute.parse(courseTime);
    }

    /**
     * 获取教练的课程预约情况
     * @param date
     * @return
     */
    public List<Course> getCoachCourse(Integer id, String date) {
        try {
            Date courseDate;
            if (StringUtils.isBlank(date)){
                long l = new Date().getTime() + 1000 * 3600L * ParameterUtil.CourseChangeTime;
                courseDate = DateUtil.YearToDay.parse(DateUtil.YearToDay.format(new Date(l)));
            }else {
                courseDate = DateUtil.YearToDay.parse(date);
            }
            return getCoachCourse(id, courseDate);
        }catch (ParseException e){
            throw new DrivingSchoolException("日期格式异常", e);
        }catch (Exception e){
            throw new SystemException(e);
        }
    }

    /**
     * 获取教练的课程预约情况
     * @param date
     * @return
     */
    @Override
    public List<Course> getCoachCourse(Integer id, Date date) {
        try {
            QueryWrapper<Course> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("coach_userid", id).eq("course_date", date);
            List<Course> courses = courseMapper.selectList(queryWrapper);
            return courses;
        }catch (Exception e){
            throw new SystemException(e);
        }
    }

    /**
     * 获取课程可预约日期
     * @return
     */
    @Override
    public List<String> getCourseAppointDate(){
        try {
            int appointCourseNumber = ParameterUtil.appointCourseNumber;
            List<String> courseDateList = new ArrayList<>(appointCourseNumber);
            long l = new Date().getTime();
            for (int i = 0; i < appointCourseNumber; i++) {
                l += 1000 * 3600L * 24;
                String courseAppointDate = DateUtil.YearToDay.format(new Date(l));
                courseDateList.add(courseAppointDate);
            }
            return courseDateList;
        }catch (Exception e){
            throw new SystemException(e);
        }
    }

    /**
     * 预约课程
     * @param course
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public ResultCode appointCourse(Course course){
        try {
            Integer studentUserid = UserUtil.userIdThreadLocal.get();
            Integer coachUserid = course.getCoachUserid();
            QueryWrapper<Course> courseQueryWrapper = new QueryWrapper<>();
            courseQueryWrapper.eq("student_userid", studentUserid);
            int appointCourseNumber = courseMapper.selectCount(courseQueryWrapper).intValue();

            // 存储日志
            String operator = UserUtil.usernameThreadLocal.get();
            String coachName = userMapper.selectById(coachUserid).getUsername();
            String msg = String.format("用户名为%s的学员预约用户名为%s的教练的课程", operator, coachName);

            // 预约课程数量限制
            if (appointCourseNumber >= ParameterUtil.appointCourseNumber) {
                String drivingSchoolLog = JSONObject.toJSONString(new DrivingSchoolLog(operator, msg, false));
                log.info(drivingSchoolLog);
                return ResultCode.COURSE_APPOINT_NUMBER_FAILED;
            }
            // 剩余课时是否足够
            QueryWrapper<Coach> coachQueryWrapper = new QueryWrapper<>();
            coachQueryWrapper.eq("userid", coachUserid);
            Coach coach = coachMapper.selectOne(coachQueryWrapper);
            int subject = coach.getSubject().getCode();
            QueryWrapper<Student> studentQueryWrapper = new QueryWrapper<>();
            studentQueryWrapper.eq("userid", studentUserid);
            Student student = studentMapper.selectOne(studentQueryWrapper);
            Integer subjectTime;
            switch (subject) {
                case 1:
                    subjectTime = student.getSubjectOneTime();
                    student.setSubjectOneTime(subjectTime - 1);
                    break;
                case 2:
                    subjectTime = student.getSubjectTwoTime();
                    student.setSubjectTwoTime(subjectTime - 1);
                    break;
                case 3:
                    subjectTime = student.getSubjectThreeTime();
                    student.setSubjectThreeTime(subjectTime - 1);
                    break;
                case 4:
                    subjectTime = student.getSubjectFourTime();
                    student.setSubjectFourTime(subjectTime - 1);
                    break;
                default:
                    throw new IllegalStateException("Unexpected value: " + subject);
            }
            if (subjectTime <= 0) {
                String drivingSchoolLog = JSONObject.toJSONString(new DrivingSchoolLog(operator, msg, false));
                log.info(drivingSchoolLog);
                return ResultCode.COURSE_APPOINT_SUBJECT_TIME_FAILED;
            }

            // 学员和教练关联
            QueryWrapper<StudentCoach> studentCoachQueryWrapper = new QueryWrapper<>();
            studentCoachQueryWrapper.eq("student_id", studentUserid).eq("coach_id", coachUserid).eq("subject", subject);
            int i1 = studentCoachMapper.selectCount(studentCoachQueryWrapper).intValue();
            if (i1 == 0) {
                StudentCoach studentCoach = new StudentCoach();
                studentCoach.setStudentId(studentUserid);
                studentCoach.setCoachId(coachUserid);
                studentCoach.setSubject(subject);
                // 设置学员教练关联
                int insert = studentCoachMapper.insert(studentCoach);
                coach.setStudentsAllNumber(coach.getStudentsAllNumber() + 1);
                // 更新教练的学员人数
                int update = coachMapper.updateById(coach);
                if (insert == 0 || update == 0){
                    String drivingSchoolLog = JSONObject.toJSONString(new DrivingSchoolLog(operator, msg, false));
                    log.info(drivingSchoolLog);
                    TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                    return ResultCode.COURSE_APPOINT_FAILED;
                }
            }

            // 预约课程
            course.setStudentUserid(studentUserid);
            int insert = courseMapper.insert(course);
            // 扣除课时
            int update = studentMapper.updateById(student);
            if (insert == 0 || update == 0){
                String drivingSchoolLog = JSONObject.toJSONString(new DrivingSchoolLog(operator, msg, false));
                log.info(drivingSchoolLog);
                TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                return ResultCode.COURSE_APPOINT_FAILED;
            }
            String drivingSchoolLog = JSONObject.toJSONString(new DrivingSchoolLog(operator, msg, true));
            log.info(drivingSchoolLog);
            return ResultCode.COURSE_APPOINT_SUCCESS;
        }catch (DrivingSchoolException e){
            throw new DrivingSchoolException(ResultCode.COURSE_APPOINT_FAILED);
        }catch (Exception e){
            throw new SystemException(e);
        }
    }

    /**
     * 删除已完成课程
     */
    @Override
    public void removeCompleteCourse() {
        try {
            QueryWrapper<Course> queryWrapper = new QueryWrapper<>();
            Date date = new Date();
            Date courseDate = DateUtil.YearToDay.parse(DateUtil.YearToDay.format(date));
            Calendar calendar = Calendar.getInstance();
            int hour = calendar.get(Calendar.HOUR_OF_DAY);
            int coursePeriod = 0;
            switch (hour){
                case 9:
                    coursePeriod = 1;
                case 10:
                    coursePeriod = 2;
                case 11:
                    coursePeriod = 3;
                case 12:
                    coursePeriod = 4;
                case 15:
                    coursePeriod = 5;
                case 16:
                    coursePeriod = 6;
                case 17:
                    coursePeriod = 7;
                case 18:
                    coursePeriod = 8;

            }
            queryWrapper.eq("course_date", courseDate).eq("course_period", coursePeriod);
            int delete = courseMapper.delete(queryWrapper);
            if (delete != 0){
                String msg = String.format("成功删除%d个已完成课程", delete);
                String drivingSchoolLog = JSONObject.toJSONString(new DrivingSchoolLog("系统消息", msg, true));
                log.info(drivingSchoolLog);
            }
        }catch (ParseException e){
            throw new DrivingSchoolException("日期格式错误", e);
        }catch (Exception e){
            throw new SystemException(e);
        }
    }

    /**
     * 教练获取预约学员
     * @param date
     * @return
     */
    @Override
    public List<CourseListVo> getCoachCourse(String date) {
        try {
            Integer coachId = UserUtil.userIdThreadLocal.get();
            Date courseDate;
            if (StringUtils.isBlank(date)){
                courseDate = DateUtil.YearToDay.parse(DateUtil.YearToDay.format(new Date()));
            }else {
                courseDate = DateUtil.YearToDay.parse(date);
            }
            List<Course> coachCourse = getCoachCourse(courseDate);

            Set<CourseListVo> courseSet = new TreeSet<>(new Comparator<CourseListVo>() {
                @Override
                public int compare(CourseListVo o1, CourseListVo o2) {
                    return o1.getCoursePeriod().getCode().compareTo(o2.getCoursePeriod().getCode());
                }
            });
            for (Course course : coachCourse) {
                CourseListVo courseListVo = new CourseListVo();
                BeanUtils.copyProperties(course, courseListVo);
                if (courseListVo.getStudentUserid() != 0) {
                    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
                    queryWrapper.eq("id", courseListVo.getStudentUserid());
                    String name = userMapper.selectOne(queryWrapper).getName();
                    courseListVo.setStudentName(name);
                }else {
                    courseListVo.setStudentName("已取消");
                }
                courseSet.add(courseListVo);
            }

            courseSet.add(new CourseListVo(null, coachId, CoursePeriodEnum.ONE_PERIOD, null, null, courseDate));
            courseSet.add(new CourseListVo(null, coachId, CoursePeriodEnum.TWO_PERIOD, null, null, courseDate));
            courseSet.add(new CourseListVo(null, coachId, CoursePeriodEnum.THREE_PERIOD, null, null, courseDate));
            courseSet.add(new CourseListVo(null, coachId, CoursePeriodEnum.FOUR_PERIOD, null, null, courseDate));
            courseSet.add(new CourseListVo(null, coachId, CoursePeriodEnum.FIVE_PERIOD, null, null, courseDate));
            courseSet.add(new CourseListVo(null, coachId, CoursePeriodEnum.SIX_PERIOD, null, null, courseDate));
            courseSet.add(new CourseListVo(null, coachId, CoursePeriodEnum.SEVEN_PERIOD, null, null, courseDate));
            courseSet.add(new CourseListVo(null, coachId, CoursePeriodEnum.EIGHT_PERIOD, null, null, courseDate));
            CourseListVo[] courses = new CourseListVo[8];
            List<CourseListVo> courseList = Arrays.asList(courseSet.toArray(courses));
            return courseList;

        }catch (ParseException e){
            throw new DrivingSchoolException("日期格式错误", e);
        }catch (Exception e){
            throw new SystemException(e);
        }
    }

    /**
     * 教练获取预约学员
     * @param date
     * @return
     */
    @Override
    public List<Course> getCoachCourse(Date date) {
        try {
            Integer id = UserUtil.userIdThreadLocal.get();
            QueryWrapper<Course> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("coach_userid", id).eq("course_date", date);
            List<Course> courses = courseMapper.selectList(queryWrapper);
            return courses;
        }catch (Exception e){
            throw new SystemException(e);
        }
    }

    /**
     * 获取课程修改日期
     * @return
     */
    @Override
    public List<String> getCourseUpdateDate(){
        try {
            int updateCourseNumber = ParameterUtil.updateCourseNumber;
            List<String> courseDateList = new ArrayList<>(updateCourseNumber);
            long l = new Date().getTime();
            for (int i = 0; i < updateCourseNumber; i++) {
                String courseAppointDate = DateUtil.YearToDay.format(new Date(l));
                courseDateList.add(courseAppointDate);
                l += 1000 * 3600L * 24;
            }
            return courseDateList;
        }catch (Exception e){
            throw new SystemException(e);
        }
    }

    /**
     * 取消预约
     * @param courseListVo
     * @return
     */
    @Override
    public ResultCode closeAppoint(CourseListVo courseListVo) {
        Course course = new Course();
        BeanUtils.copyProperties(courseListVo, course);
        course.setStudentUserid(0);
        int insert = courseMapper.insert(course);

        // 存储日志
        String operator = UserUtil.usernameThreadLocal.get();
        String msg = String.format("用户名为%s的教练取消了课程的预约", operator);

        if (insert != 0){
            String drivingSchoolLog = JSONObject.toJSONString(new DrivingSchoolLog(operator, msg, true));
            log.info(drivingSchoolLog);
            return ResultCode.COURSE_CLOSE_APPOINT_SUCCESS;
        }else {
            String drivingSchoolLog = JSONObject.toJSONString(new DrivingSchoolLog(operator, msg, false));
            log.info(drivingSchoolLog);
            return ResultCode.COURSE_CLOSE_APPOINT_FAILED;
        }
    }

    /**
     * 开启预约
     * @param courseListVo
     * @return
     */
    @Override
    public ResultCode openAppoint(CourseListVo courseListVo) {
        int delete = courseMapper.deleteById(courseListVo.getId());

        // 存储日志
        String operator = UserUtil.usernameThreadLocal.get();
        String msg = String.format("用户名为%s的教练取消了课程的预约", operator);

        if (delete != 0){
            String drivingSchoolLog = JSONObject.toJSONString(new DrivingSchoolLog(operator, msg, true));
            log.info(drivingSchoolLog);
            return ResultCode.COURSE_OPEN_APPOINT_SUCCESS;
        }else {
            String drivingSchoolLog = JSONObject.toJSONString(new DrivingSchoolLog(operator, msg, false));
            log.info(drivingSchoolLog);
            return ResultCode.COURSE_OPEN_APPOINT_FAILED;
        }
    }
}
